// 导入React相关hooks
import React, { useRef, useState } from "react";
// 导入SparkMD5用于计算文件哈希值
import SparkMD5 from "spark-md5";
// 导入axios用于HTTP请求
import axios from "axios";
// 导入Ant Design组件
import { Button, Progress, Upload, Typography, Space, message } from "antd";
// 导入Ant Design图标
import {
    UploadOutlined,
    PauseOutlined,
    PlayCircleOutlined,
} from "@ant-design/icons";
import { useThemeContext } from "../../../app/ThemeContext";

// 定义常量
const CHUNK_SIZE = 2 * 1024 * 1024; // 切片大小：2MB
const MAX_CONCURRENT = 3; // 最大并发上传数量

// 定义切片类型接口
type Chunk = {
    file: Blob; // 切片文件数据
    index: number; // 切片索引
    start: number; // 切片在原始文件中的起始位置
    end: number; // 切片在原始文件中的结束位置
    progress: number; // 切片上传进度
};

// 定义取消令牌类型
type CancelToken = ReturnType<typeof axios.CancelToken.source>;

// 定义上传信息类型
type UploadInfo = {
    target?: { files?: FileList };
    file?: File;
};

/**
 * 大文件切片上传组件
 * 支持断点续传、秒传、并发上传等功能
 */
const Order: React.FC = () => {
    const { theme } = useThemeContext();
    // 状态管理
    const [file, setFile] = useState<File | null>(null); // 当前选择的文件
    const [hash, setHash] = useState<string>(""); // 文件哈希值
    const [chunks, setChunks] = useState<Chunk[]>([]); // 文件切片数组
    const [progress, setProgress] = useState<number>(0); // 整体上传进度
    const [uploading, setUploading] = useState(false); // 是否正在上传
    const [paused, setPaused] = useState(false); // 是否暂停上传

    // 存储取消令牌的引用，用于取消上传
    const cancelTokens = useRef<CancelToken[]>([]);

    /**
     * 处理文件选择
     * @param info - 上传信息对象
     */
    const handleFileChange = async (info: UploadInfo) => {
        // 获取选择的文件
        const f = info?.target?.files?.[0] || info?.file;
        if (!f) return;

        // 重置状态
        setFile(f);
        setProgress(0);
        setPaused(false);
        setUploading(false);

        // 1. 计算文件哈希值
        message.loading({ content: "正在计算文件Hash...", key: "hash" });
        const hash = await calculateHash(f);
        setHash(hash);
        message.success({ content: "Hash计算完成", key: "hash", duration: 1 });

        // 2. 创建文件切片
        const chunks = createChunks(f);
        setChunks(chunks);

        // 3. 检查文件是否已上传（断点续传/秒传）
        try {
            const checkRes = await checkFile(hash, f.name);
            if (checkRes.uploaded) {
                setProgress(100);
                message.success("文件已秒传！");
                return;
            }
            // uploadedChunks 仅用于过滤，未用可省略
        } catch {
            message.error("检查文件状态失败");
        }
    };

    /**
     * 计算文件哈希值
     * 使用SparkMD5计算文件的MD5哈希，用于文件唯一标识
     * @param file - 要计算哈希的文件
     * @returns Promise<string> - 文件的MD5哈希值
     */
    const calculateHash = (file: File) => {
        return new Promise<string>((resolve, reject) => {
            const spark = new SparkMD5.ArrayBuffer(); // 创建SparkMD5实例
            const reader = new FileReader();
            const chunkSize = CHUNK_SIZE;
            const chunks = Math.ceil(file.size / chunkSize); // 计算切片数量
            let currentChunk = 0;

            /**
             * 读取下一个切片
             */
            function loadNext() {
                const start = currentChunk * chunkSize;
                const end = Math.min(file.size, start + chunkSize);
                reader.readAsArrayBuffer(file.slice(start, end));
            }

            // 文件读取完成时的处理
            reader.onload = (e) => {
                spark.append(e.target?.result as ArrayBuffer); // 将切片数据添加到哈希计算中
                currentChunk++;
                if (currentChunk < chunks) {
                    loadNext(); // 继续读取下一个切片
                } else {
                    resolve(spark.end()); // 完成哈希计算
                }
            };
            reader.onerror = reject;
            loadNext(); // 开始读取第一个切片
        });
    };

    /**
     * 创建文件切片
     * 将大文件分割成多个小切片
     * @param file - 要切片的文件
     * @returns Chunk[] - 切片数组
     */
    const createChunks = (file: File): Chunk[] => {
        const chunks: Chunk[] = [];
        let cur = 0,
            idx = 0;

        // 循环创建切片
        while (cur < file.size) {
            chunks.push({
                file: file.slice(cur, cur + CHUNK_SIZE), // 创建切片Blob
                index: idx++,
                start: cur,
                end: Math.min(cur + CHUNK_SIZE, file.size),
                progress: 0,
            });
            cur += CHUNK_SIZE;
        }
        return chunks;
    };

    /**
     * 检查文件上传状态
     * 用于实现秒传和断点续传功能
     * @param hash - 文件哈希值
     * @param filename - 文件名
     * @returns Promise - 检查结果
     */
    const checkFile = async (hash: string, filename: string) => {
        const res = await axios.post(
            "/upload/check",
            {
                fileHash: hash,
                filename,
            },
            {
                timeout: 30000, // 设置30秒超时
            }
        );
        // 返回 { uploaded: boolean, uploadedChunks: string[] }
        return res.data;
    };

    /**
     * 上传单个切片
     * @param chunk - 要上传的切片
     * @param hash - 文件哈希值
     * @returns Promise - 上传结果
     */
    const uploadChunk = (chunk: Chunk, hash: string) => {
        // 创建FormData对象
        const formData = new FormData();
        formData.append("chunk", chunk.file);
        formData.append("fileHash", hash);
        formData.append("chunkIndex", chunk.index.toString());

        // 创建取消令牌
        const source = axios.CancelToken.source();
        cancelTokens.current[chunk.index] = source;

        // 发送上传请求
        return axios.post("/upload/chunk", formData, {
            timeout: 60000, // 设置60秒超时（文件上传需要更长时间）
            onUploadProgress: (e) => {
                // 更新切片上传进度
                chunk.progress = e.total ? (e.loaded / e.total) * 100 : 0;
                setChunks((prev) => [...prev]); // 触发重新渲染
            },
            cancelToken: source.token, // 设置取消令牌
        });
    };

    /**
     * 开始并发上传
     * 使用控制并发数量的方式上传所有切片
     */
    const startUpload = async () => {
        if (!file || !hash || chunks.length === 0) return;

        setUploading(true);
        setPaused(false);

        // 检查已上传的切片
        const checkRes = await checkFile(hash, file.name);
        if (checkRes.uploaded) {
            setProgress(100);
            message.success("文件已秒传！");
            setUploading(false);
            return;
        }

        // 获取已上传的切片索引
        const uploadedChunks = (checkRes.uploadedChunks || []).map(Number);

        // 过滤出需要上传的切片
        const needUpload = chunks.filter(
            (c) => !uploadedChunks.includes(c.index)
        );

        let finished = uploadedChunks.length; // 已完成的数量
        const total = chunks.length; // 总数量

        let pool: Promise<unknown>[] = []; // 并发池
        let i = 0; // 当前处理的切片索引
        let hasError = false; // 是否有错误

        /**
         * 运行并发上传
         */
        const run = async () => {
            // 当还有切片需要上传，且并发池未满，且未暂停，且无错误时继续
            while (
                i < needUpload.length &&
                pool.length < MAX_CONCURRENT &&
                !paused &&
                !hasError
            ) {
                const chunk = needUpload[i++];
                const p = uploadChunk(chunk, hash)
                    .then(() => {
                        finished++;
                        setProgress(Math.round((finished / total) * 100));
                    })
                    .catch((err) => {
                        if (!axios.isCancel(err)) {
                            hasError = true;
                            message.error("上传失败: " + err.message);
                            setUploading(false);
                        }
                    })
                    .finally(() => {
                        // 从并发池中移除已完成的请求
                        pool = pool.filter((item) => item !== p);
                        // 如果未暂停且无错误，继续运行
                        if (!paused && !hasError) run();
                    });
                pool.push(p);
            }

            // 所有切片上传完成后，合并文件
            if (finished === total && !paused && !hasError) {
                // 合并切片
                await axios.post(
                    "/upload/merge",
                    {
                        fileHash: hash,
                        filename: file.name,
                        chunkSize: CHUNK_SIZE,
                    },
                    {
                        timeout: 30000, // 设置30秒超时
                    }
                );
                setUploading(false);
                setProgress(100);
                message.success("上传完成！");
            }
        };

        run(); // 开始运行
    };

    /**
     * 暂停上传
     * 取消所有正在进行的上传请求
     */
    const handlePause = () => {
        setPaused(true);
        // 取消所有正在进行的请求
        cancelTokens.current.forEach(
            (token) => token && token.cancel && token.cancel()
        );
        setUploading(false);
    };

    /**
     * 恢复上传
     * 重新开始上传过程
     */
    const handleResume = () => {
        setPaused(false);
        startUpload();
    };

    // 主题自适应背景色和卡片色
    const bg =
        theme === "dark"
            ? "linear-gradient(120deg, #181c27 0%, #232526 100%)"
            : "linear-gradient(120deg, #f5f6fa 0%, #e9ecef 100%)";
    const cardBg =
        theme === "dark" ? "rgba(30,32,40,0.98)" : "rgba(255,255,255,0.98)";
    const cardShadow =
        theme === "dark"
            ? "0 8px 32px rgba(0,0,0,0.38)"
            : "0 8px 32px rgba(0,0,0,0.12)";

    // 渲染UI界面
    return (
        <div
            style={{
                height: "100%",
                width: "100%",
                background: bg,
                display: "flex",
                alignItems: "center", // 垂直居中
                justifyContent: "center", // 水平居中
                padding: 0,
                margin: 0,
            }}
        >
            <div
                style={{
                    width: 600,
                    minHeight: 700,
                    background: cardBg,
                    borderRadius: 24,
                    boxShadow: cardShadow,
                    padding: 48,
                    display: "flex",
                    flexDirection: "column",
                    alignItems: "center",
                    justifyContent: "center",
                    margin: 0,
                }}
            >
                {/* 标题 */}
                <Typography.Title
                    level={3}
                    style={{ textAlign: "center", marginBottom: 32 }}
                >
                    大文件切片上传
                </Typography.Title>

                {/* 文件选择区域 */}
                <Upload
                    beforeUpload={(file) => {
                        handleFileChange({ file });
                        return false; // 阻止默认上传行为
                    }}
                    showUploadList={false}
                    accept="*"
                    disabled={uploading}
                    style={{ width: "100%" }}
                >
                    <Button
                        icon={<UploadOutlined />}
                        block
                        size="large"
                        disabled={uploading}
                        style={{ marginBottom: 24 }}
                    >
                        选择文件
                    </Button>
                </Upload>

                {/* 文件信息和上传控制区域 */}
                {file && (
                    <Space
                        direction="vertical"
                        style={{ width: "100%", marginTop: 16 }}
                        size="large"
                    >
                        {/* 文件信息显示 */}
                        <Typography.Text strong>
                            文件名: {file.name}
                        </Typography.Text>
                        <Typography.Text type="secondary">
                            大小: {(file.size / 1024 / 1024).toFixed(2)} MB
                        </Typography.Text>

                        {/* 进度条 */}
                        <Progress
                            percent={progress}
                            status={
                                progress === 100
                                    ? "success"
                                    : uploading
                                    ? "active"
                                    : "normal"
                            }
                            showInfo
                        />

                        {/* 控制按钮 */}
                        <Space
                            style={{ width: "100%", justifyContent: "center" }}
                        >
                            <Button
                                type="primary"
                                onClick={startUpload}
                                disabled={uploading || progress === 100}
                            >
                                {progress === 100 ? "已完成" : "开始上传"}
                            </Button>
                            <Button
                                icon={<PauseOutlined />}
                                onClick={handlePause}
                                disabled={!uploading || paused}
                            >
                                暂停
                            </Button>
                            <Button
                                icon={<PlayCircleOutlined />}
                                onClick={handleResume}
                                disabled={
                                    uploading || !paused || progress === 100
                                }
                            >
                                恢复
                            </Button>
                        </Space>
                    </Space>
                )}
            </div>
        </div>
    );
};

export default Order;
